// ============================================================================
//
// LCP-net framework by Pierre CHATEL chatelp@gmail.com
// Distributed under GPLv3
// Copyright (C) THALES Land & Joint Systems, 2008, 2009. All rights reserved
//
// ============================================================================

/**
 * <copyright>
 * </copyright>
 *
 * $Id$
 */
package com.thalesgroup.preferenceFramework.models.LCPnet.impl;

import java.util.Collection;

import net.sourceforge.jFuzzyLogic.rule.FuzzyRule;
import net.sourceforge.jFuzzyLogic.rule.FuzzyRuleExpression;
import net.sourceforge.jFuzzyLogic.rule.FuzzyRuleSet;
import net.sourceforge.jFuzzyLogic.rule.FuzzyRuleTerm;
import net.sourceforge.jFuzzyLogic.rule.Variable;
import net.sourceforge.jFuzzyLogic.ruleConnection.RuleConnectionMethod;
import net.sourceforge.jFuzzyLogic.ruleConnection.RuleConnectionMethodAndMin;
import net.sourceforge.jFuzzyLogic.ruleImplication.RuleImplicationMethod;
import net.sourceforge.jFuzzyLogic.ruleImplication.RuleImplicationMethodMin;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.InternalEObject;

import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.impl.EObjectImpl;

import org.eclipse.emf.ecore.util.EObjectContainmentWithInverseEList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.InternalEList;

import com.thalesgroup.preferenceFramework.models.LCPnet.CLPT;
import com.thalesgroup.preferenceFramework.models.LCPnet.CLPTLine;
import com.thalesgroup.preferenceFramework.models.LCPnet.LCPnetPackage;
import com.thalesgroup.preferenceFramework.models.LCPnet.LNode;
import com.thalesgroup.preferenceFramework.models.LCPnet.LNodeValue;
import com.thalesgroup.preferenceFramework.models.LCPnet.LNodeValueUtility;
import com.thalesgroup.preferenceFramework.models.LCPnet.LinguisticSubset;
import com.thalesgroup.preferenceFramework.models.LCPnet.exceptions.LCPnetEvaluationException;
import com.thalesgroup.preferenceFramework.models.LCPnet.exceptions.LNodeEvaluationException;

/**
 * <!-- begin-user-doc --> An implementation of the model object '
 * <em><b>CLPT</b></em>'. <!-- end-user-doc -->
 * <p>
 * The following features are implemented:
 * <ul>
 *   <li>{@link com.thalesgroup.preferenceFramework.models.LCPnet.impl.CLPTImpl#getName <em>Name</em>}</li>
 *   <li>{@link com.thalesgroup.preferenceFramework.models.LCPnet.impl.CLPTImpl#getLines <em>Lines</em>}</li>
 *   <li>{@link com.thalesgroup.preferenceFramework.models.LCPnet.impl.CLPTImpl#getNode <em>Node</em>}</li>
 * </ul>
 * </p>
 *
 * @generated
 */
public class CLPTImpl extends EObjectImpl implements CLPT {
	/**
	 * The default value of the '{@link #getName() <em>Name</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getName()
	 * @generated
	 * @ordered
	 */
	protected static final String NAME_EDEFAULT = null;
	/**
	 * The cached value of the '{@link #getName() <em>Name</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getName()
	 * @generated
	 * @ordered
	 */
	protected String name = NAME_EDEFAULT;

	/**
	 * The cached value of the '{@link #getLines() <em>Lines</em>}' containment reference list.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @see #getLines()
	 * @generated
	 * @ordered
	 */
	protected EList<CLPTLine> lines;

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	protected CLPTImpl() {
		super();
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	protected EClass eStaticClass() {
		return LCPnetPackage.Literals.CLPT;
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public EList<CLPTLine> getLines() {
		if (lines == null) {
			lines = new EObjectContainmentWithInverseEList<CLPTLine>(CLPTLine.class, this, LCPnetPackage.CLPT__LINES, LCPnetPackage.CLPT_LINE__TABLE);
		}
		return lines;
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public LNode getNode() {
		if (eContainerFeatureID != LCPnetPackage.CLPT__NODE) return null;
		return (LNode)eContainer();
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public NotificationChain basicSetNode(LNode newNode, NotificationChain msgs) {
		msgs = eBasicSetContainer((InternalEObject)newNode, LCPnetPackage.CLPT__NODE, msgs);
		return msgs;
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public void setNode(LNode newNode) {
		if (newNode != eInternalContainer() || (eContainerFeatureID != LCPnetPackage.CLPT__NODE && newNode != null)) {
			if (EcoreUtil.isAncestor(this, newNode))
				throw new IllegalArgumentException("Recursive containment not allowed for " + toString());
			NotificationChain msgs = null;
			if (eInternalContainer() != null)
				msgs = eBasicRemoveFromContainer(msgs);
			if (newNode != null)
				msgs = ((InternalEObject)newNode).eInverseAdd(this, LCPnetPackage.LNODE__LINGUISTIC_TABLE, LNode.class, msgs);
			msgs = basicSetNode(newNode, msgs);
			if (msgs != null) msgs.dispatch();
		}
		else if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, LCPnetPackage.CLPT__NODE, newNode, newNode));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public String getName() {
		return name;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setName(String newName) {
		String oldName = name;
		name = newName;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, LCPnetPackage.CLPT__NAME, oldName, name));
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@SuppressWarnings("unchecked")
	@Override
	public NotificationChain eInverseAdd(InternalEObject otherEnd,
			int featureID, NotificationChain msgs) {
		switch (featureID) {
			case LCPnetPackage.CLPT__LINES:
				return ((InternalEList<InternalEObject>)(InternalEList<?>)getLines()).basicAdd(otherEnd, msgs);
			case LCPnetPackage.CLPT__NODE:
				if (eInternalContainer() != null)
					msgs = eBasicRemoveFromContainer(msgs);
				return basicSetNode((LNode)otherEnd, msgs);
		}
		return super.eInverseAdd(otherEnd, featureID, msgs);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public NotificationChain eInverseRemove(InternalEObject otherEnd,
			int featureID, NotificationChain msgs) {
		switch (featureID) {
			case LCPnetPackage.CLPT__LINES:
				return ((InternalEList<?>)getLines()).basicRemove(otherEnd, msgs);
			case LCPnetPackage.CLPT__NODE:
				return basicSetNode(null, msgs);
		}
		return super.eInverseRemove(otherEnd, featureID, msgs);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public NotificationChain eBasicRemoveFromContainerFeature(
			NotificationChain msgs) {
		switch (eContainerFeatureID) {
			case LCPnetPackage.CLPT__NODE:
				return eInternalContainer().eInverseRemove(this, LCPnetPackage.LNODE__LINGUISTIC_TABLE, LNode.class, msgs);
		}
		return super.eBasicRemoveFromContainerFeature(msgs);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public Object eGet(int featureID, boolean resolve, boolean coreType) {
		switch (featureID) {
			case LCPnetPackage.CLPT__NAME:
				return getName();
			case LCPnetPackage.CLPT__LINES:
				return getLines();
			case LCPnetPackage.CLPT__NODE:
				return getNode();
		}
		return super.eGet(featureID, resolve, coreType);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void eSet(int featureID, Object newValue) {
		switch (featureID) {
			case LCPnetPackage.CLPT__NAME:
				setName((String)newValue);
				return;
			case LCPnetPackage.CLPT__LINES:
				getLines().clear();
				getLines().addAll((Collection<? extends CLPTLine>)newValue);
				return;
			case LCPnetPackage.CLPT__NODE:
				setNode((LNode)newValue);
				return;
		}
		super.eSet(featureID, newValue);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eUnset(int featureID) {
		switch (featureID) {
			case LCPnetPackage.CLPT__NAME:
				setName(NAME_EDEFAULT);
				return;
			case LCPnetPackage.CLPT__LINES:
				getLines().clear();
				return;
			case LCPnetPackage.CLPT__NODE:
				setNode((LNode)null);
				return;
		}
		super.eUnset(featureID);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public boolean eIsSet(int featureID) {
		switch (featureID) {
			case LCPnetPackage.CLPT__NAME:
				return NAME_EDEFAULT == null ? name != null : !NAME_EDEFAULT.equals(name);
			case LCPnetPackage.CLPT__LINES:
				return lines != null && !lines.isEmpty();
			case LCPnetPackage.CLPT__NODE:
				return getNode() != null;
		}
		return super.eIsSet(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public String toString() {
		if (eIsProxy()) return super.toString();

		StringBuffer result = new StringBuffer(super.toString());
		result.append(" (name: ");
		result.append(name);
		result.append(')');
		return result.toString();
	}

	public void setRules(FuzzyRuleSet ruleSet) throws LNodeEvaluationException {
		// Defaults methods for connection and implication
		RuleConnectionMethod andMethod = new RuleConnectionMethodAndMin();
		RuleImplicationMethod ruleImplicationMethod = new RuleImplicationMethodMin();

		ruleSet.setRuleImplicationMethod(ruleImplicationMethod);

		EList<CLPTLine> lines = this.getLines();
		int ruleNumber = 0;

		// For each cell of the CLPT -> 1 fuzzy rule
		for (CLPTLine line : lines) {

			EList<LNodeValueUtility> nodeValueUtilities = line.getUtility();

			// For each cell in this line
			for (LNodeValueUtility nodeValueUtility : nodeValueUtilities) {
				LNodeValue thisNodeValue = nodeValueUtility.getNodeValue();
				LinguisticSubset utility = nodeValueUtility.getUtility();

				// convert to a fuzzy rule
				FuzzyRule fuzzyRule = new FuzzyRule("rule_" + ruleNumber++);

				// Deduct rule's antecedents from input node values
				BasicEList<LNodeValue> intputNodeValues = new BasicEList<LNodeValue>(
						line.getParentValues());
				intputNodeValues.add(thisNodeValue);

				fuzzyRule.setAntecedents(this.generateRuleAntecedents(
						intputNodeValues, ruleSet, andMethod));

				// Deduct rule's consequent from cell utility value
				String varName = utility.getDomain().getName();
				String lingTerm = utility.getName();

				Variable variable = ruleSet.getVariable(varName);
				if (variable == null)
					throw new LNodeEvaluationException("Variable " + varName
							+ " does not exist");

				fuzzyRule.addConsequent(variable, lingTerm, false);

				// add rule to node's fuzzy rule set
				ruleSet.add(fuzzyRule);
			}
		}
	}

	/**
	 * Recursively generate a rule's antecedents (IF a = b AND c = d then) from
	 * the following parameters:
	 * 
	 * @param intputNodeValues
	 *            input nodes values of the current node.
	 * @param ruleSet
	 *            the rule set to initialize.
	 * @param andMethod
	 *            the 'and method' used between each term.
	 * @return
	 * @throws LNodeEvaluationException 
	 */
	private FuzzyRuleExpression generateRuleAntecedents(
			EList<LNodeValue> intputNodeValues, FuzzyRuleSet ruleSet,
			RuleConnectionMethod andMethod) throws LNodeEvaluationException {
		FuzzyRuleExpression fuzzyRuleExpression = new FuzzyRuleExpression();

		if (intputNodeValues.size() > 1) {
			fuzzyRuleExpression.setRuleConnectionMethod(andMethod);

			LNodeValue firstNodeValue = intputNodeValues.get(0);
			BasicEList<LNodeValue> firstNodeValueAsList = new BasicEList<LNodeValue>();
			firstNodeValueAsList.add(firstNodeValue);

			BasicEList<LNodeValue> remainingNodeValues = new BasicEList<LNodeValue>(
					intputNodeValues.subList(1, intputNodeValues.size()));

			// Recurse
			fuzzyRuleExpression.setTerm1(generateRuleAntecedents(
					firstNodeValueAsList, ruleSet, andMethod));
			fuzzyRuleExpression.setTerm2(generateRuleAntecedents(
					remainingNodeValues, ruleSet, andMethod));
		} else {
			LNodeValue nodeValue = intputNodeValues.get(0);
			String varName = nodeValue.getNode().getName();
			String lingTerm = nodeValue.getLinguisticValue().getName();

			Variable variable = ruleSet.getVariable(varName);
			if (variable == null)
				throw new LNodeEvaluationException("Variable " + varName
						+ " does not exist");

			FuzzyRuleTerm fuzzyRuleTerm = new FuzzyRuleTerm(variable, lingTerm,
					false);
			fuzzyRuleExpression.add(fuzzyRuleTerm);
		}

		return fuzzyRuleExpression;
	}
} // CLPTImpl
